Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit c59e50a24e6306c8fe0d9962071fdc6a2f88f5d3


Parents : d51ad49
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-04-23T04:44:25-05:00

feat(linkUtils): add httpUrlHrefOrNull method for safe URL normalization and update NomadNetworkPage to utilize it

Changes
Diff

diff --git a/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue b/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue
index 8427ee8d..0aa93236 100644
--- a/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue
+++ b/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue
@@ -444,6 +444,7 @@
<script>
import MicronParser from "../../js/MicronParser";
+import LinkUtils from "../../js/LinkUtils";
import { renderNomadPageByPath } from "../../js/NomadPageRenderer";
import DialogUtils from "../../js/DialogUtils";
import WebSocketConnection from "../../js/WebSocketConnection";
@@ -1625,9 +1626,10 @@ export default {
console.log(fieldData);
- // open http urls in new tab
- if (url.startsWith("http://") || url.startsWith("https://")) {
- window.open(url, "_blank");
+ const httpHref =
+ typeof url === "string" ? LinkUtils.httpUrlHrefOrNull(url.trim()) : null;
+ if (httpHref) {
+ window.open(httpHref, "_blank", "noopener,noreferrer");
return;
}

diff --git a/meshchatx/src/frontend/js/LinkUtils.js b/meshchatx/src/frontend/js/LinkUtils.js
index e9d2264a..e2cef075 100644
--- a/meshchatx/src/frontend/js/LinkUtils.js
+++ b/meshchatx/src/frontend/js/LinkUtils.js
@@ -25,6 +25,13 @@ function httpUrlHrefOrNull(core) {
}
export default class LinkUtils {
+ /**
+ * Returns canonical http(s) href or null if the string is not a safe remote URL.
+ */
+ static httpUrlHrefOrNull(core) {
+ return httpUrlHrefOrNull(core);
+ }
+
static protectAnchors(text) {
const anchors = [];
const protectedText = text.replace(/<a\b[^>]*>[\s\S]*?<\/a>/gi, (anchor) => {

diff --git a/tests/frontend/LinkUtils.test.js b/tests/frontend/LinkUtils.test.js
index d7e973bf..10ccdef2 100644
--- a/tests/frontend/LinkUtils.test.js
+++ b/tests/frontend/LinkUtils.test.js
@@ -163,4 +163,21 @@ describe("LinkUtils.js", () => {
expect(Date.now() - start).toBeLessThan(200);
});
});
+
+ describe("httpUrlHrefOrNull", () => {
+ it("returns canonical https href", () => {
+ expect(LinkUtils.httpUrlHrefOrNull("https://example.com/path")).toBe(
+ "https://example.com/path"
+ );
+ });
+
+ it("returns null for javascript: payloads that start with https-looking junk", () => {
+ expect(LinkUtils.httpUrlHrefOrNull("https://example.com javascript:alert(1)")).toBeNull();
+ });
+
+ it("returns null for non-http schemes", () => {
+ expect(LinkUtils.httpUrlHrefOrNull("javascript:alert(1)")).toBeNull();
+ expect(LinkUtils.httpUrlHrefOrNull("file:///etc/passwd")).toBeNull();
+ });
+ });
});


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────